package com.example.glideprogressiveloadimg

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.util.Log
import com.example.glideprogressiveloadimg.util.ProgressiveJpegParser
import com.example.glideprogressiveloadimg.util.TailAppendingInputStream
import okhttp3.MediaType
import okhttp3.ResponseBody
import okio.Buffer
import okio.BufferedSource
import okio.ForwardingSource
import okio.Okio
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream

class ProgressResponseBody()
    : ResponseBody() {

    val TAG = "ProgressResponseBody"

    var bufferedSource : BufferedSource?=null

    var responseBody: ResponseBody?=null

    var listener: ProgressListener?=null

    constructor(url: String, body: ResponseBody?) : this() {
        this.responseBody = body
        this.listener = ProgressInterceptor.LISTENER_MAP.get(url)
    }

    override fun contentType(): MediaType? {
        return responseBody?.contentType()
    }

    override fun contentLength(): Long {
        return responseBody?.contentLength() ?:0
    }

    override fun source(): BufferedSource {
        Log.d(TAG, "source()")
        if (bufferedSource == null) {
            bufferedSource = Okio.buffer(ProgressSource(responseBody?.source()))
        }
        return bufferedSource!!
    }

    inner class ProgressSource(source: BufferedSource?)
        : ForwardingSource(source) {

        var totalBytesRead = 0L

        var currentProgress:Int=0

        var fullLength = 0L

        val data:ByteArrayOutputStream = ByteArrayOutputStream()
        var frameCount = 0
        val progressiveParser = ProgressiveJpegParser()
        var oldBestScanNumber = 0

        override fun read(sink: Buffer, byteCount: Long): Long {
            val bytesRead =  super.read(sink, byteCount)
            fullLength = responseBody!!.contentLength()
            if (bytesRead == -1L) {
                totalBytesRead = fullLength
            }else {
                totalBytesRead += bytesRead
            }

            val tmp = Buffer()
            sink.copyTo(tmp, 0, sink.size())
            val bitmap: Bitmap? = handleProgressive(tmp)

            val progress = (100f * totalBytesRead / fullLength).toInt()
            if (listener != null && progress != currentProgress) {
                listener!!.onLoadProgress(progress >=100, progress, bitmap, frameCount)
            }

            if (listener != null && totalBytesRead == fullLength) {
                listener = null
            }

            currentProgress = progress
            return bytesRead
        }

        fun handleProgressive(buff:Buffer):Bitmap? {
            Log.d(TAG, "handleRead :: ")
            if (fullLength <= 0) return null

            val buffSize = buff.size()
            buff.writeTo(data, buffSize)
            buff.flush()
            Log.d(TAG, "data size is buffSize = ${buffSize}")

            val toByteArray = data.toByteArray()

            //--------------------------------------------------------------------------------//
            progressiveParser.parseMoreData(ByteArrayInputStream(toByteArray))
            val bestScanNumber = progressiveParser.getBestScanNumber()
            val bestScanEndOffset = progressiveParser.getBestScanEndOffset()

            if (bestScanNumber > 0 && bestScanEndOffset > 0) {
                if (bestScanNumber > oldBestScanNumber) {
                    Log.d(TAG, "bestScanNumber = $bestScanNumber, bestScanEndOffset = $bestScanEndOffset")
                    oldBestScanNumber = bestScanNumber
                    frameCount = bestScanNumber

                    val tailAppendingInputStream = TailAppendingInputStream(
                        ByteArrayInputStream(toByteArray, 0, bestScanEndOffset),
                            TailAppendingInputStream.EOI_TAIL)

                    val bitmap = BitmapFactory.decodeStream(tailAppendingInputStream)
                    tailAppendingInputStream.close()
                    return bitmap
                }
            }
            return null
        }

    }

}
